<!-- 控件视图-渲染 -->
<template>
    <template v-if="!hiddenStatus">
        <template v-if="!preview">
            <div
                v-if="!free"
                ref="widgetDomRef"
                :id="`widget-view-${widgetId}`"
                :class="[{
                    'active': selectStatus
                }, animationName]"
                :style="[animationStyle]"
                class="widget-view"
                @mouseover.stop="onMouseover"
                @mouseout.stop="onMouseout"
                @mousedown.stop="onMousedown"
                @contextmenu.prevent.stop="onContextmenu"
                @animationend="onAnimationEnd"
            >
                <div class="widget-view-content">
                    <component
                        v-if="widgetTag"
                        :is="widgetTag"
                        :element="element"
                        :id="widgetId"
                    />
                    <div v-else>没有找到{{ element.eles.type }}组件</div>
                    <widget-view-border class="border-wrap" />
                </div>
                <div class="widget-view-btn">
                    <div
                        v-if="!hideDragBtn"
                        class="move-btn"
                        :class="[draggableHandleName]"
                    >
                        <el-icon color="#409EFF" :size="16"><icon-menu /></el-icon>
                    </div>
                    <div
                        v-if="showHideBtn"
                        title="隐藏"
                        class="hide-btn"
                        @mousedown.stop="() => ({})"
                        @click.stop.prevent="onHide"
                    >
                        <el-icon color="#409EFF"><icon-minus /></el-icon>
                    </div>
                    <div
                        v-if="!hideDeleteBtn"
                        title="删除"
                        class="close-btn"
                        @mousedown.stop="() => ({})"
                        @click.stop.prevent="onDelete"
                    >
                        <el-icon color="#409EFF"><icon-close-bold /></el-icon>
                    </div>
                </div>
            </div>
            <div
                v-else
                ref="widgetDomRef"
                :id="`widget-view-${widgetId}`"
                :style="freeStyle"
                :class="{
                    'active': selectStatus
                }"
                class="widget-view free"
                @mouseover.stop="onMouseover"
                @mouseout.stop="onMouseout"
                @mousedown.stop="onFreeMousedown"
                @contextmenu.prevent.stop="onContextmenu"
            >
                <div class="widget-view-content">
                    <component
                        v-if="widgetTag"
                        :is="widgetTag"
                        :element="element"
                        :id="widgetId"
                    />
                    <div v-else>没有找到{{ element.eles.type }}组件</div>
                    <widget-view-border class="border-wrap" />
                    <widget-view-resizable
                        :element="element"
                        :left="freeLeft"
                        :top="freeTop"
                        :width="freeWidth"
                        :height="freeHeight"
                        :max-width="freeParentWidth"
                        :max-height="freeParentHeight"
                        :resizable="freeResizable"
                        @start="onFreeResizableStart"
                        @move="onFreeResizableMove"
                        @end="onFreeResizableEnd"
                    />
                </div>
            </div>
        </template>

        <!-- 预览模式 -->
        <template v-else>
            <div
                :id="`widget-view-preview-${widgetId}`"
                ref="widgetDomRef"
                :style="[free ? freeStyle : {}]"
                :class="{
                    'free': free
                }"
                class="widget-view preview"
                @mouseover.stop="onMouseover"
                @mouseout.stop="onMouseout"
                @mousedown.stop="handleSelectWidget"
            >
                <div class="widget-view-content">
                    <component
                        v-if="widgetTag"
                        :is="widgetTag"
                        :element="element"
                        :id="widgetId"
                        :preview="preview"
                    />
                    <div v-else>没有找到{{ element.eles.type }}组件</div>
                    <widget-view-border class="border-wrap" />
                </div>
            </div>
        </template>
    </template>
</template>

<script>
import { defineComponent, toRefs, ref, computed, watch, onMounted, onUnmounted } from 'vue'
import useWidget from '@/libs/edit/widgetData'
import usePageHistory from '@/libs/edit/pageHistory'
import usePhoneZoom from '@/libs/edit/phoneZoom'
import useWidgetDraggable from '@/libs/edit/widgetDraggable'
import bus from '@/libs/bus'
import { openWidgetContextmenu } from '@/components/edit/widget-contextmenu'
import { widgetTags, widgetComponents } from './components/widget-views'
import WidgetViewBorder from './components/widget-view-border'
import WidgetViewResizable from './components/widget-view-resizable'
export default defineComponent({
    name: 'WidgetView',
    components: {
        ...widgetComponents,
        WidgetViewBorder,
        WidgetViewResizable
    },
    props: {
        element: {
            type: Object,
            default: () => ({})
        },
        // 自由控件模式
        free: {
            type: Boolean,
            default: false
        },
        // 自由控件父容器dom
        freeParent: {
            type: Object,
            default: () => ({})
        },
        // 禁用
        disabled: {
            type: Boolean,
            default: false
        },
        // 预览模式
        preview: {
            type: Boolean,
            default: false
        },
        // 隐藏拖拽按钮
        hideDragBtn: {
            type: Boolean,
            default: false
        },
        // 隐藏删除按钮
        hideDeleteBtn: {
            type: Boolean,
            default: false
        },
        // 隐藏右键菜单
        hideContextmenu: {
            type: Boolean,
            default: false
        }
    },
    emits: [
        'contextmenu',
        'selectWidget'
    ],
    setup(props, { emit }) {
        const {
            element, free, freeParent, disabled, preview,
            hideDragBtn, hideDeleteBtn, hideContextmenu
        } = toRefs(props)

        const {
            widgetListFlat,
            selectWidgetIds,
            onSelectWidget,
            onDeleteWidget,
            onClickWidget
        } = useWidget()

        const {
            onAddPageHistory
        } = usePageHistory()

        const {
            getZoomNum
        } = usePhoneZoom()

        const {
            draggableHandleName
        } = useWidgetDraggable()

        const widgetDomRef = ref(null)
        const widgetTag = computed(() => widgetTags[element.value.eles.type])
        const widgetId = computed(() => element.value.eles.id)
        const selectStatus = computed(() => selectWidgetIds.value.includes(widgetId.value))
        const hiddenStatus = computed(() => !!element.value?.eles?.hidden)
        const showHideBtn = computed(() => ['sidebar', 'popup-window'].includes(element.value.eles.type))

        const topWidgetId = widgetListFlat.value[widgetId.value]?.hierarchy?.[0]
        const handleSelectWidget = () => {
            if (!topWidgetId) return
            bus.emit(topWidgetId, widgetId.value)
        }
        onMounted(() => {
            if (preview.value && !!topWidgetId) {
                bus.on(topWidgetId, id => {
                    emit('selectWidget', id)
                })
            }
            // 动画开始
            onAnimationStart()
        })
        onUnmounted(() => {
            if (preview.value && !!topWidgetId) {
                bus.off(topWidgetId)
            }
        })

        // 按下控件时
        const onMousedown = e => {
            onSelect(e)
        }
        // 移入移出控件时
        const onMouseover = e => {
            widgetDomRef.value.classList.add('is-hover')
        }
        // 移出控件时
        const onMouseout = e => {
            widgetDomRef.value.classList.remove('is-hover')
        }
        // 右键菜单
        const onContextmenu = e => {
            if (hideContextmenu.value) return
            const { clientX, clientY } = e
            if (!selectStatus.value) {
                const widgetIds = [widgetId.value]
                onSelectWidget(widgetIds)
                openWidgetContextmenu({
                    left: clientX,
                    top: clientY,
                    widgetIds
                })
            } else {
                const widgetIds = [...new Set([...selectWidgetIds.value, widgetId.value])]
                openWidgetContextmenu({
                    left: clientX,
                    top: clientY,
                    widgetIds
                })
            }
        }

        // 选中控件
        const onSelect = e => {
            onClickWidget(e, widgetId.value)
        }

        // 隐藏控件
        const onHide = e => {
            element.value.eles.customFeature.widgetHidden = true
        }

        // 删除控件
        const onDelete = e => {
            onDeleteWidget([widgetId.value])
            onAddPageHistory()
        }

        // 自由控件属性
        const freeWidgetDom = computed(() => widgetDomRef.value?.children?.[0]?.children?.[0])
        const freeLeft = computed({
            get: () => parseInt(element.value.eles.style.left),
            set: val => element.value.eles.style.left = `${val}px`
        })
        const freeTop = computed({
            get: () => parseInt(element.value.eles.style.top),
            set: val => element.value.eles.style.top = `${val}px`
        })
        const freeWidth = computed({
            get: () => parseInt(element.value.eles.style.width) || widgetDomRef.value?.offsetWidth,
            set: val => element.value.eles.style.width = `${val}px`
        })
        const freeHeight = computed({
            get: () => parseInt(element.value.eles.style.height) || widgetDomRef.value?.offsetHeight,
            set: val => {
                element.value.eles.style.height = `${val}px`
                if (element.value.eles.style['line-height']) {
                    element.value.eles.style['line-height'] = `${val}px`
                }
            }
        })
        const freeStyle = computed(() => {
            const style = element.value.eles.style
            return {
                position: style.position,
                left: style.left,
                top: style.top
            }
        })
        // 自由控件可调整大小配置
        const freeResizable = computed(() => {
            const style = element.value.eles.style
            return {
                left: style.hasOwnProperty('left'),
                top: style.hasOwnProperty('top'),
                width: style.hasOwnProperty('width'),
                height: style.hasOwnProperty('height')
            }
        })
        // 自由控件父容器属性
        const freeParentWidth = ref(Infinity)
        const freeParentHeight = ref(Infinity)
        // 开始调整自由控件大小时触发
        const onFreeResizableStart = () => {
            const { offsetWidth, offsetHeight } = freeParent.value
            freeParentWidth.value = parseInt(offsetWidth)
            freeParentHeight.value = parseInt(offsetHeight)
        }
        // 拖动调整自由控件大小时触发
        const onFreeResizableMove = data => {
            const { left, top, width, height } = data
            widgetDomRef.value.style.cssText += `left:${left}px;top:${top}px;`
            freeWidgetDom.value.style.cssText += `width:${width}px;height:${height}px;${!!element.value.eles.style['line-height'] ? `line-height:${height}px;` : ''}`
        }
        // 结束调整自由控件大小时触发
        const onFreeResizableEnd = data => {
            const { left, top, width, height } = data
            freeLeft.value = left
            freeTop.value = top
            freeWidth.value = width
            freeHeight.value = height
        }
        // 自由控件拖动
        const onFreeMousedown = e => {
            if (disabled.value) return
            e = e || window.event
            // 选中控件
            onSelect(e)
            // 鼠标按下时坐标
            const { clientX: mouseX, clientY: mouseY } = e
            // 拖动控件的宽高和坐标
            const style = element.value.eles.style
            let { left: domL, top: domT } = style
            let { offsetWidth: domW, offsetHeight: domH } = widgetDomRef.value
            domL = parseInt(domL)
            domT = parseInt(domT)
            domW = parseInt(domW)
            domH = parseInt(domH)
            // 父容器宽高
            let { offsetWidth: parentW, offsetHeight: parentH } = freeParent.value
            parentW = parseInt(parentW)
            parentH = parseInt(parentH)
            // 更新后dom坐标
            let newDomL = domL
            let newDomT = domT
            let frame = null
            // 拖动状态
            let dragStatus = false
            // 窗口鼠标移动事件
            document.onmousemove = event => {
                dragStatus = true
                newDomL = parseInt(getZoomNum(event.clientX - mouseX) + domL)
                newDomT = parseInt(getZoomNum(event.clientY - mouseY) + domT)
                // 限制拖拽范围
                if (newDomL < 0) newDomL = 0
                if (newDomL + domW > parentW) newDomL = parentW - domW
                if (newDomT < 0) newDomT = 0
                if (newDomT + domH > parentH) newDomT = parentH - domH
                // 更新视图
                frame = requestAnimationFrame(() => {
                    widgetDomRef.value.style.cssText += `left:${newDomL}px;top:${newDomT}px;`
                })
            }

            document.onmouseup = () => {
                // 更新数据
                style.left = newDomL + 'px'
                style.top = newDomT + 'px'
                if (dragStatus) onAddPageHistory()
                dragStatus = false
                cancelAnimationFrame(frame)
                document.onmousemove = null
                document.onmouseup = null
            }
        }

        // 控件动画
        const animationIndex = ref(0)
        const animationName = ref('')
        const animationStyle = ref({})
        const animations = computed(() => element.value.eles.animations || [])
        const animation = computed(() => animations.value[animationIndex.value] || null)
        // 监听动画改变
        watch(
            () => animations.value,
            () => {
                animationIndex.value = 0
                onAnimationStart()
            }
        )
        // 动画开始
        const onAnimationStart = () => {
            if (!animation.value) return
            const {
                name = '',
                direction = '',
                delay = 0,
                duration = 0,
                count = 0
            } = animation.value
            animationName.value = ''
            animationStyle.value = {}
            setTimeout(() => {
                animationName.value = `animate__animated animate__${name + direction}`
                animationStyle.value = {
                    animationPlayState: 'running',
                    animationDelay: `${delay}s`,
                    animationDuration: `${duration}s`,
                    animationIterationCount: count
                }
            }, 10)
        }
        // 执行下一个动画
        const onAnimationNext = () => {
            animationIndex.value += 1
            onAnimationStart()
        }
        // 动画结束
        const onAnimationEnd = () => {
            if (animationIndex.value >= animations.value.length - 1) {
                animationName.value = ''
                return
            }
            onAnimationNext()
        }

        return {
            element,
            free,
            preview,
            hideDragBtn,
            hideDeleteBtn,

            draggableHandleName,

            widgetDomRef,
            widgetTag,
            widgetId,
            selectStatus,
            hiddenStatus,
            showHideBtn,

            handleSelectWidget,

            onMousedown,
            onMouseover,
            onMouseout,
            onContextmenu,
            
            onSelect,
            onHide,
            onDelete,

            freeLeft,
            freeTop,
            freeWidth,
            freeHeight,
            freeStyle,
            freeResizable,
            freeParentWidth,
            freeParentHeight,
            onFreeResizableStart,
            onFreeResizableMove,
            onFreeResizableEnd,
            onFreeMousedown,

            animationName,
            animationStyle,
            onAnimationEnd
        }
    }
})
</script>

<style lang="less" scoped>
@import './widget-view.less';
</style>